#include "head.h"

client clients[30];
int local = 1, port;
int unix_socket, inet_socket;
void add(char name[32], int local, struct sockaddr * address, socklen_t address_length) {
	int i = 0;
	while (strlen(clients[i].name) > 0 && i < 30) {
		i++;
	}
	if (i < 30) {
		strcpy(clients[i].name, name);
		clients[i].local = local;
		clients[i].address_size = address_length;
		if (local) {
			memcpy(&(clients[i].unix_address), (struct sockaddr_un *)address, address_length);
		} else {
			memcpy(&(clients[i].inet_address), (struct sockaddr_in *)address, address_length);
		}
	} else {
		printf("Nie ma już miejsca\n");
	}
}


void remove_cl(char name[32]) {
	int i = 0;
	while (i < 30 && strcmp(clients[i].name, name)) {
		i++;
	}
	if (i >= 30) {
		printf("Klient nie istnieje\n");
	}
	while (i + 1 < 30) {
		strcpy(clients[i].name, clients[i+1].name);
		clients[i].address_size = clients[i+1].address_size;
		clients[i].local = clients[i+1].local;
		if (clients[i+1].local) {
			memcpy(&(clients[i].unix_address), &(clients[i+1].unix_address), clients[i+1].address_size);
		} else {
			memcpy(&(clients[i].inet_address), &(clients[i+1].inet_address), clients[i+1].address_size);
		}
		i++;
	}
	clients[i].name[0] = 0;
}


void send_list(int sockfd, const struct sockaddr * destination, socklen_t address_length) {
	list lista;
	int i;
	for (i = 0; i < 30; i++) {
		strcpy(lista.array[i], clients[i].name);
	}
	if (sendto(sockfd, (void *)&lista, sizeof(lista), 0, destination, address_length) == -1) {
		perror("sendto");
		exit(3);
	}
}


int main(int argc, char ** argv) {
	if (argc != 3) {
		printf("Niepoprawna liczba argumentów. Poprawne wywołanie: ./server.o <numer_portu> <sciezka_do_socketu>\n");
		return -1;
	}
	int port = atoi(argv[1]);
	if (port <= 0) {
		printf("Port musi być liczbą całkowitą dodatnią.\n");
		return -2;
	}
	char * path = argv[2];
	if ((unix_socket = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1) {
		perror("socket unix");
		return -3;
	}
	if ((inet_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
		perror("socket inet");
		return -4;
	}
	struct sockaddr_un srv_unix_address, cl_unix_address;
	struct sockaddr_in srv_inet_address, cl_inet_address;
	srv_unix_address.sun_family = AF_UNIX;
	strcpy(srv_unix_address.sun_path, path);
	srv_inet_address.sin_family = AF_INET;
	srv_inet_address.sin_addr.s_addr = htonl(INADDR_ANY);
	srv_inet_address.sin_port = htons(port);
	if (bind(unix_socket, (struct sockaddr *)&srv_unix_address, sizeof(struct sockaddr_un)) == -1) {
		perror("bind unix");
		return -5;
	}
	if (bind(inet_socket, (struct sockaddr *)&srv_inet_address, sizeof(struct sockaddr_in)) == -1) {
		perror("bind inet");
		return -6;
	}
	int i;
	for (i = 0; i < 30; i++) {
		clients[i].name[0] = 0;
	}
	task_type request;
	socklen_t unix_addr_size = sizeof(struct sockaddr_un), inet_addr_size = sizeof(struct sockaddr_in);
	while(1) {
		while(1) {
			if (recvfrom(unix_socket, (void *)&request, sizeof(request), MSG_DONTWAIT, (struct sockaddr *)&cl_unix_address, &unix_addr_size) > 0) {
				local = 1;
				break;
			}
			if (recvfrom(inet_socket, (void *)&request, sizeof(request), MSG_DONTWAIT, (struct sockaddr *)&cl_inet_address, &inet_addr_size) > 0) {
				local = 0;
				break;
			}
			sleep(1);
		}
		if (request.action <= 1) {
			if (local) {
				if (request.action == 0) {
					add(request.name, local, (struct sockaddr *)&cl_unix_address, unix_addr_size);
				}
				send_list(unix_socket, (struct sockaddr *)&cl_unix_address, unix_addr_size);
			} else {
				if (request.action == 0) {
					add(request.name, local, (struct sockaddr *)&cl_inet_address, inet_addr_size);
				}
				send_list(inet_socket, (struct sockaddr *)&cl_inet_address, inet_addr_size);
			}
		} else if (request.action == 2) {
			printf("Żądanie polecenia: %s\n",request.command);
			task_type request;
			request.action = 2;
			char output[2000];
			int sockfd;
			struct sockaddr * destination;
			socklen_t des_length;
			int client_number = request.client;
			des_length = clients[client_number].address_size;
			if (clients[client_number].local) {
				sockfd = unix_socket;
				destination = (struct sockaddr *)&(clients[client_number].unix_address);
			} else {
				sockfd = inet_socket;
				destination = (struct sockaddr *)&(clients[client_number].inet_address);
				char ip[16];
				inet_ntop(AF_INET, &(clients[client_number].inet_address.sin_addr.s_addr), ip, 16);
			}
			if (sendto(sockfd, (void *)&request, sizeof(request), 0, destination, des_length) == -1) {
				perror("sendto 1");
				return -10;
			}
			int i, flag = 1;
			for (i = 0; i < 2000 && flag == 1; i++) {
				sleep(1);
				if (recv(sockfd, (void *)output, sizeof(char)*2000, MSG_DONTWAIT) == -1) {
					if (errno != EAGAIN && errno != EWOULDBLOCK) {
						perror("recv");
						return -11;
					} else {
						continue;
					}
				}
				flag = 0;
			}
			if (flag == 1) {
				printf("Klient nie odpowiada\n");
			} else {
				if (local) {
					if (sendto(unix_socket, (void *)&output, sizeof(output), 0, (struct sockaddr *)&cl_unix_address, unix_addr_size) == -1) {
						perror("sendto 2");
						return -13;
					}
				} else {
					if (sendto(inet_socket, (void *)&output, sizeof(output), 0, (struct sockaddr *)&cl_inet_address, inet_addr_size) == -1) {
						perror("sendto 2");
						return -14;
					}
				}
			}
		} else if (request.action == 3) {
			remove_cl(request.name);
		} else {
			printf("Żadanie nieznane\n");
		}
	}
	return 0;
}
